package com.jz.netthings.netty.utils;

import com.jz.netthings.netty.protocol.bean.ZxdcLoginPacket;
import com.jz.netthings.netty.protocol.bean.ZxdcMsgPacket;
import com.jz.netthings.netty.support.Attributes;
import com.jz.netthings.netty.support.ServerHandlerSupport;
import com.jz.netthings.utils.XmlHelper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.CharsetUtil;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 负责将和单采机交互的xml和实体类的互转
 */
public class NettyMsgHelper {

    static Logger logger = LoggerFactory.getLogger(NettyMsgHelper.class);

    /**
     * 记录分配浆员到采浆机成功的集合
     */
    private static Map<ChannelHandlerContext,Boolean> allotSuccessMap = new HashMap<>();
    /**
     * 记录取消分配浆员到采浆机成功的集合
     */
    private static Map<ChannelHandlerContext,Boolean> cancelAllotSuccessMap = new HashMap<>();
    /**
     * 记录平板发送认证成功的消息给单采机，单采机回传的信息
     */
    private static Map<ChannelHandlerContext,ChannelHandlerContext> chairMacMap = new HashMap<>();

    public static Map<ChannelHandlerContext, ChannelHandlerContext> getChairMacMap() {
        return chairMacMap;
    }

    public static void setChairMacMap(Map<ChannelHandlerContext, ChannelHandlerContext> chairMacMap) {
        NettyMsgHelper.chairMacMap = chairMacMap;
    }

    public static Map<ChannelHandlerContext, Boolean> getAllotSuccessMap() {
        return allotSuccessMap;
    }

    public static void setAllotSuccessMap(Map<ChannelHandlerContext, Boolean> allotSuccessMap) {
        NettyMsgHelper.allotSuccessMap = allotSuccessMap;
    }

    public static Map<ChannelHandlerContext, Boolean> getCancelAllotSuccessMap() {
        return cancelAllotSuccessMap;
    }

    public static void setCancelAllotSuccessMap(Map<ChannelHandlerContext, Boolean> cancelAllotSuccessMap) {
        NettyMsgHelper.cancelAllotSuccessMap = cancelAllotSuccessMap;
    }

    /**
     * 对象转字节数组
     *
     * @param packet
     * @return
     */
    public static byte[] packetToBytes(ZxdcMsgPacket packet) {
        byte[] byteArray = null;

        try {

            HashMap<String, Object> datParams = new HashMap();
            datParams.put("seq", packet.getSeq());
            datParams.put("cmd", packet.getCmd());
            datParams.put("level", Integer.valueOf(packet.getLevel()));
            if (packet.getResponseFromCmd() != null) {
                datParams.put("responseFromCmd", packet.getResponseFromCmd());
            }
            if (packet.getTimeout() > 0L) {
                datParams.put("timeout", Long.valueOf(packet.getTimeout()));
            }
            if (packet.getParam() != null) {
                datParams.put("param", packet.getParam());
            }
            if (packet.getValues() != null) {
                datParams.put("values", packet.getValues());
            }
            if (packet.isHasResponse()) {
                datParams.put("hasResponse", "true");
            }
            if (packet.isConcurrent()) {
                datParams.put("concurrent", "true");
            }
            if (packet.getRelayAp() != null) {
                datParams.put("relayAp", packet.getRelayAp());
            }
            if (packet.getRelayOrg() != null) {
                datParams.put("relayOrg", packet.getRelayOrg());
            }
            ByteArrayOutputStream byte_out = new ByteArrayOutputStream();
            try {
                ObjectOutputStream mywriter = new ObjectOutputStream(byte_out);
                try {
                    mywriter.writeObject(datParams);
                    byteArray = byte_out.toByteArray();
                } finally {
                    mywriter.close();
                }
            } finally {
                byte_out.close();
            }
            return byteArray;

        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        return byteArray;
    }

    /**\
     * 字节 转对象
     * @param data
     * @return
     */
    public static ZxdcMsgPacket bytesToPacket(byte[] data) {
        ZxdcMsgPacket packet = null;

        try {
            packet = new ZxdcMsgPacket();

            ByteArrayInputStream byte_in = new ByteArrayInputStream(data, 0, data.length);
            Object s_obj;
            try {
                ObjectInputStream myreader = new ObjectInputStream(byte_in);
                try {
                    s_obj = myreader.readObject();
                } finally {
                    myreader.close();
                }
            } finally {
                byte_in.close();
            }
            if (!(s_obj instanceof HashMap)) {
                return packet;
            }

            packet.setSeq((String) ((HashMap) s_obj).get("seq"));
            packet.setCmd((String) ((HashMap) s_obj).get("cmd"));
            packet.setResponseFromCmd((String) ((HashMap) s_obj).get("responseFromCmd"));
            packet.setLevel((Integer) ((HashMap) s_obj).get("level"));
            Long to = (Long) ((HashMap) s_obj).get("timeout");
            if (to != null) {
                packet.setTimeout(to.longValue());
            }
            packet.setParam((String) ((HashMap) s_obj).get("param"));
            packet.setValues((HashMap) ((HashMap) s_obj).get("values"));
            packet.setHasResponse("true".equals(((HashMap) s_obj).get("hasResponse")));
            packet.setConcurrent("true".equals(((HashMap) s_obj).get("concurrent")));
            packet.setRelayAp((String) ((HashMap) s_obj).get("relayAp"));
            packet.setRelayOrg((String) ((HashMap) s_obj).get("relayOrg"));

        } catch (Exception e) {
            logger.error("bytes 转 packet 对象失败...", e);
        }

        return packet;
    }


    /**
     * 对象转xml
     *
     * @param packet
     * @return
     */
    public static byte[] packetToXml(ZxdcMsgPacket packet) {
        byte[] bytes;
        try {
            Element content = new Element("Msg");
            String seq = packet.getSeq();
            if ( seq == null || "".equals(seq)){
                seq = UUID.randomUUID().toString();
            }
            XmlHelper.writeValueTagHasType(content, "seq", seq);
            XmlHelper.writeValueTagHasType(content, "cmd", packet.getCmd());
            XmlHelper.writeValueTagHasType(content, "level", Integer.valueOf(packet.getLevel()));
            if (packet.getResponseFromCmd() != null) {
                XmlHelper.writeValueTagHasType(content, "responseFromCmd", packet.getResponseFromCmd());
            }
            if (packet.getTimeout() > 0L) {
                XmlHelper.writeValueTagHasType(content, "timeout", Long.valueOf(packet.getTimeout()));
            }
            if (packet.getParam() != null) {
                XmlHelper.writeValueTagHasType(content, "param", packet.getParam());
            }
            if (packet.getValues() != null) {
                XmlHelper.writeValueTagHasType(content, "values", packet.getValues());
            }
            if (packet.getTaskList() != null) {
                XmlHelper.writeValueTagHasType(content, "taskList", packet.getTaskList());
            }
            if (packet.isHasResponse()) {
                XmlHelper.writeValueTagHasType(content, "hasResponse", Boolean.valueOf(packet.isHasResponse()));
            }
            if (packet.isConcurrent()) {
                XmlHelper.writeValueTagHasType(content, "concurrent", Boolean.valueOf(packet.isConcurrent()));
            }
            if (packet.getRelayAp() != null) {
                XmlHelper.writeValueTagHasType(content, "relayAp", packet.getRelayAp());
            }
            if (packet.getRelayOrg() != null) {
                XmlHelper.writeValueTagHasType(content, "relayOrg", packet.getRelayOrg());
            }
            Format format = Format.getRawFormat();
            format.setEncoding("GB2312");
            format.setLineSeparator(System.getProperty("line.separator"));
            XMLOutputter outputter = new XMLOutputter();
            outputter.setFormat(format);
            Document doc = new Document(content);
            StringWriter out_buffer = new StringWriter();
            outputter.output(doc, out_buffer);
            bytes = out_buffer.getBuffer().toString().getBytes();

            FileOutputStream out = null;
            try {
                String fileName = UUID.randomUUID().toString();
                File f = new File("D:\\jiaying\\allotXml\\"+ fileName +".xml");

                if(!f.exists()){
                    f.createNewFile();
                }
                out = new FileOutputStream(f);
                out.write(bytes, 0, bytes.length);
                out.flush();

            } catch (Exception e) {
                System.out.println("写文件失败~~~："+e.getMessage());
            }finally{
                if(out != null){
                    out.close();
                }
            }

        } catch (Exception e) {
            logger.error(" packet 对象转 xml 失败...", e);
            bytes = null;
        }
        return bytes;
    }

    /**
     * xml转对象
     *
     * @param data xml的字节数组
     * @return
     */
    public static ZxdcMsgPacket xmlToPacket(byte[] data){

        ZxdcMsgPacket packet = null;

        try {
            packet = new ZxdcMsgPacket();

            SAXBuilder docbuilder = new SAXBuilder();
            StringReader cmd_in_buffer = new StringReader(new String(data, CharsetUtil.US_ASCII));
            Document doc = docbuilder.build(cmd_in_buffer);
            Element content = doc.getRootElement();

            packet.setSeq(((String) XmlHelper.readValueTagHasType(content, "seq")));
            packet.setCmd(((String) XmlHelper.readValueTagHasType(content, "cmd")));
            packet.setResponseFromCmd(((String) XmlHelper.readValueTagHasType(content, "responseFromCmd")));

            Object v = XmlHelper.readValueTagHasType(content, "level");
            if ((v instanceof Integer)) {
                packet.setLevel(((Integer) v).intValue());
            }
            v = XmlHelper.readValueTagHasType(content, "timeout");
            if ((v instanceof Long)) {
                packet.setTimeout(((Long) v).longValue());
            }
            packet.setParam(((String) XmlHelper.readValueTagHasType(content, "param")));
            packet.setValues(((HashMap) XmlHelper.readValueTagHasType(content, "values")));
            v = XmlHelper.readValueTagHasType(content, "hasResponse");
            if ((v instanceof Boolean)) {
                packet.setHasResponse(((Boolean) v).booleanValue());
            }
            v = XmlHelper.readValueTagHasType(content, "concurrent");
            if ((v instanceof Boolean)) {
                packet.setConcurrent(((Boolean) v).booleanValue());
            }
            packet.setRelayAp(((String) XmlHelper.readValueTagHasType(content, "relayAp")));
            packet.setRelayOrg(((String) XmlHelper.readValueTagHasType(content, "relayOrg")));

        } catch (Exception e) {
            logger.error("xml 转 packet 对象失败："+e.getMessage());
        }

        return packet;

    }

    /**
     * 发送给单采机的包，转换的xml字节流
     * @param ctx
     * @param packet
     */
    public static void sendPacket(ChannelHandlerContext ctx, ZxdcMsgPacket packet){

        byte[] xmlBytes = NettyMsgHelper.packetToXml(packet);
        NettyMsgHelper.sendPacket(ctx,xmlBytes);
    }

    /**
     * 发送给平板的包，转换成的obj的字节流
     * @param ctx
     * @param packet
     */
    public static void sendPacketToChair(ChannelHandlerContext ctx, ZxdcMsgPacket packet){

        byte[] objBytes = NettyMsgHelper.packetToBytes(packet);
        NettyMsgHelper.sendPacket(ctx,objBytes);

    }

    private static void sendPacket(ChannelHandlerContext ctx,byte[] bytes){
        if (ctx != null){
            ByteBuf bbf = Unpooled.buffer();
            bbf.writeInt(Attributes.MSG_RES_CODE);
            bbf.writeInt(bytes.length);
            bbf.writeBytes(bytes);
            ctx.channel().writeAndFlush(bbf);
        }
    }

    /**
     * 发送分配的包
     * @param ctx
     * @param packet
     */
    public static boolean sendAllotPacket(ChannelHandlerContext ctx, ZxdcMsgPacket packet){

        Map<ChannelHandlerContext, Boolean> map = NettyMsgHelper.getAllotSuccessMap();
        return sendPacket(map,ctx,packet);
    }
    /**
     * 发送取消浆员分配的包
     * @param ctx
     * @param packet
     */
    public static boolean sendCancelDonorPacket(ChannelHandlerContext ctx, ZxdcMsgPacket packet){

        Map<ChannelHandlerContext, Boolean> map = NettyMsgHelper.getCancelAllotSuccessMap();
        return sendPacket(map,ctx,packet);
//        NettyMsgHelper.sendPacket(ctx,packet);
//        try{
//            Thread.sleep(2000);
//        } catch (InterruptedException e) {}
//        return true;
    }

    private static boolean sendPacket(Map<ChannelHandlerContext, Boolean> map,ChannelHandlerContext ctx, ZxdcMsgPacket packet){
        map.put(ctx,false);

        boolean flag = false;
        try {
            for (int i=0;i< 3;i++){
                NettyMsgHelper.sendPacket(ctx,packet);
                Thread.sleep(2000);

                Boolean success = map.get(ctx);
                if (success){
                    flag = true;
                    break;
                }
            }
        } catch (InterruptedException e) {}

        return flag;
    }


    public static void handlerZxdcLogin(ZxdcLoginPacket packet, ServerHandlerSupport serverHandlerSupport,ChannelHandlerContext ctx){
        logger.info("登录数据：" + packet);

        packet.setCtx(ctx);
        serverHandlerSupport.handlePacket(packet);

        byte[] ok = "ok".getBytes();
        ByteBuf bbf = Unpooled.buffer();
        bbf.writeInt(Attributes.LOGIN_SUCCESS_RES_CODE);
        bbf.writeInt(ok.length);
        bbf.writeBytes(ok);
        ctx.writeAndFlush(bbf);
    }

    public static void sendOnlineMsg(ChannelHandlerContext ctx) {
        ByteBuf bbf = Unpooled.buffer();
        bbf.writeInt(Attributes.MSG_RES_CODE);
        bbf.writeInt(getOnlineXmlBytes().length);
        bbf.writeBytes(getOnlineXmlBytes());
        ctx.channel().writeAndFlush(bbf);
    }
    public static void sendOnlineMsgToChair(ChannelHandlerContext ctx) {
        ByteBuf bbf = Unpooled.buffer();
        bbf.writeInt(Attributes.MSG_RES_CODE);
        bbf.writeInt(getOnlineObjBytes().length);
        bbf.writeBytes(getOnlineObjBytes());
        ctx.channel().writeAndFlush(bbf);
    }
    public static void sendTimeStampMsgToChair(ChannelHandlerContext ctx) {
        ByteBuf bbf = Unpooled.buffer();
        bbf.writeInt(Attributes.MSG_RES_CODE);
        bbf.writeInt(getTimestampObjBytes().length);
        bbf.writeBytes(getTimestampObjBytes());
        ctx.channel().writeAndFlush(bbf);
    }
    public static void sendTimeStampMsgToMc(ChannelHandlerContext ctx) {
        ByteBuf bbf = Unpooled.buffer();
        bbf.writeInt(Attributes.MSG_RES_CODE);
        bbf.writeInt(getTimestampXmlBytes().length);
        bbf.writeBytes(getTimestampXmlBytes());
        ctx.channel().writeAndFlush(bbf);
    }

    public static byte[] getOnlineXmlBytes(){
        return BulidByte.getXmlBytes();
    }
    public static byte[] getOnlineObjBytes(){
        return BulidByte.getObjBytes();
    }
    public static byte[] getTimestampObjBytes(){
        return BulidByte.getTimestampObjBytes();
    }
    public static byte[] getTimestampXmlBytes(){
        return BulidByte.getTimestampXmlBytes();
    }

    private static class BulidByte{
        private static byte[]  xmlBytes;
        private static byte[]  objBytes;
        static{
            ZxdcMsgPacket heartPacket = new ZxdcMsgPacket();
            heartPacket.setSeq(Long.toString(System.currentTimeMillis()));
            heartPacket.setCmd("online");
            heartPacket.setLevel(1);

            BulidByte.xmlBytes = NettyMsgHelper.packetToXml(heartPacket);
            BulidByte.objBytes = NettyMsgHelper.packetToBytes(heartPacket);
        }

        static byte[] getXmlBytes(){
            return BulidByte.xmlBytes;
        }
        static byte[] getObjBytes(){
            return BulidByte.objBytes;
        }


        static ZxdcMsgPacket getTimeStampPacket(){
            ZxdcMsgPacket tsPacket = new ZxdcMsgPacket();
            tsPacket.setCmd("timestamp");
            tsPacket.setLevel(1);
            HashMap<String, Object> values = new HashMap<>();
            values.put("t",Long.toString(System.currentTimeMillis()));
            tsPacket.setValues(values);
            return tsPacket;
        }

        static byte[] getTimestampObjBytes(){
            return NettyMsgHelper.packetToBytes(getTimeStampPacket());
        }
        static byte[] getTimestampXmlBytes(){
            ZxdcMsgPacket tsPacket = getTimeStampPacket();
            tsPacket.setSeq(UUID.randomUUID().toString());

            return NettyMsgHelper.packetToXml(tsPacket);
        }
    }


    public static ZxdcLoginPacket buildRequestLoginPacket(int res_code,ByteBuf msg){
        ZxdcLoginPacket packet = new ZxdcLoginPacket();

        packet.setResCode(res_code);
        packet.setClientType(msg.readInt());
        packet.setMsgFormat(msg.readInt());

        int apCodeLen = msg.readInt();
        byte[] apCode = new byte[apCodeLen];
        msg.readBytes(apCode);//应用编码（ApCode）
        packet.setApCode(new String(apCode, CharsetUtil.US_ASCII));

        int OrgCodeLen = msg.readInt();
        byte[] orgCode = new byte[OrgCodeLen];
        msg.readBytes(orgCode);//应用所属组织（OrgCode）
        packet.setOrgCode(new String(orgCode, CharsetUtil.US_ASCII));

        int loginPwdLen = msg.readInt();
        byte[] loginPwd = new byte[loginPwdLen];
        msg.readBytes(loginPwd);//登录密码
        packet.setPwd(new String(loginPwd, CharsetUtil.US_ASCII));

        return packet;
    }


}
